package org.adorsys.aderp.aderplogin.security.token;

import javax.annotation.Resource;

import org.adorsys.aderp.aderplogin.client.ClientDetailsHelper;
import org.adorsys.aderp.aderplogin.client.common.Oauth2User;
import org.adorsys.aderp.aderplogin.domain.AderpClientDetail;
import org.adorsys.aderp.aderplogin.domain.AderpUser;
import org.adorsys.aderp.aderplogin.domain.UserRole;
import org.adorsys.aderp.aderplogin.service.AderpClientDetailService;
import org.adorsys.aderp.aderplogin.service.AderpUserService;
import org.adorsys.aderp.aderplogin.service.UserRoleService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Service;

/**
 * Load and return the information needed to create a client details 
 * in a webapp.
 * 
 * If the Authetication is expired, return null.
 * 
 * Stores an association to the requesting client. For single logout.
 * 
 * @author francis
 *
 */
@Service("oauth2TokenReplicationService")
public class InMemoryOauth2TokenReplicationService  implements Oauth2TokenReplicationService, InitializingBean {
	
	private ActiveClientStore activeClientStore = new InMemoryActiveClientStore();
	
	@Resource
	private TokenRoleAssociationStore tokenRoleAssociationStore;
	
	@Resource
	private TokenStore tokenStore;
	
	@Resource
	private AderpUserService aderpUserService;
	
	@Resource
	private AderpClientDetailService aderpClientDetailService;
	
	@Resource
	private UserRoleService userRoleService;
	
	@Override
	public Oauth2User loadUser(String token, String clientId) {
		OAuth2AccessToken readAccessToken = tokenStore.readAccessToken(token);
		OAuth2Authentication readAuthentication = tokenStore.readAuthentication(readAccessToken);
		Authentication userAuthentication = readAuthentication.getUserAuthentication();

		String selectedRole = tokenRoleAssociationStore.getSelectedRole(token);
		AuthorizationRequest authorizationRequest = readAuthentication.getAuthorizationRequest();
		
		AderpUser aderpUser = aderpUserService.findByUserName(userAuthentication.getName());
		AderpClientDetail aderpClientDetail = aderpClientDetailService.findByClientId(clientId);
		UserRole candidate = null;
		if(selectedRole!=null)
			candidate = userRoleService.findByRoleKey(selectedRole);
		
		UserRole role2Return = null;
		if (candidate!=null){
			if(authorizationRequest.getClientId().equals(clientId)){// requesting client is original client
				// verify that this role belong to the user.
				role2Return = ClientDetailsHelper.getUserRole(aderpUser, aderpClientDetail, candidate);
			} else {// we need a role match for the requesting client
				// TODO Francis: Create Client Role MAPPINGS
				role2Return = ClientDetailsHelper.getUserRole(aderpUser, aderpClientDetail, candidate);
				
			}
			
		}
		
		// store client reference.
		activeClientStore.storeActiveClient(token, clientId);
		
		Oauth2User oauth2User = new Oauth2User();
		oauth2User.setOauthToken(token);
		oauth2User.setUsername(userAuthentication.getName());
		if(role2Return!=null)
			oauth2User.setSelectedRole(role2Return.getRoleKey());

		return oauth2User;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		if(activeClientStore==null) throw new IllegalStateException("ActiveClientStore must be set");
		if(tokenStore==null) throw new IllegalStateException("TokenStore must be set.");
		
	}

	public void setActiveClientStore(ActiveClientStore activeClientStore) {
		this.activeClientStore = activeClientStore;
	}

	public void setTokenStore(TokenStore tokenStore) {
		this.tokenStore = tokenStore;
	}

}
